/* Simplistic black and white PAL encoder
 (c) 2006 Fabrice Bellard
 */
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>
#include<stdlib.h>
#include <stdio.h>
#include <inttypes.h>
#include <math.h>

#define BLACK_LEVEL  64
#define WHITE_LEVEL  206
#define SYNC_LEVEL   3

/* convert a 704x576 gray scale image into a 864 * 625 sample PAL
   signal. The PAL signal is sampled at 13.5 MHz with a 8 bit
   resolution */
void picture_to_pal(uint8_t *pal_signal, const uint8_t *img_data, 
                    int linesize)
{
    uint8_t *q;
    const uint8_t *s;
    float v;
    int y, i;

    q = pal_signal;
    for(y = 0; y < 625; y++) {
        switch(y) {
        case 0:
        case 1:
        case 5:
        case 6:
        case 312:
        case 313:
        case 317:
        case 318:
            /* EE line */
            for(i = 0; i < 32; i++)
                *q++ = SYNC_LEVEL;
            for(i = 0; i < 400; i++)
                *q++ = BLACK_LEVEL;
            for(i = 0; i < 32; i++)
                *q++ = SYNC_LEVEL;
            for(i = 0; i < 400; i++)
                *q++ = BLACK_LEVEL;
            break;
        case 2:
        case 3:
        case 315:
        case 316:
            /* SS line */
            for(i = 0; i < 369; i++)
                *q++ = SYNC_LEVEL;
            for(i = 0; i < 63; i++)
                *q++ = BLACK_LEVEL;
            for(i = 0; i < 369; i++)
                *q++ = SYNC_LEVEL;
            for(i = 0; i < 63; i++)
                *q++ = BLACK_LEVEL;
            break;
        case 4:
            /* SE line */
            for(i = 0; i < 369; i++)
                *q++ = SYNC_LEVEL;
            for(i = 0; i < 63; i++)
                *q++ = BLACK_LEVEL;
            for(i = 0; i < 32; i++)
                *q++ = SYNC_LEVEL;
            for(i = 0; i < 400; i++)
                *q++ = BLACK_LEVEL;
            break;
        case 314:
            /* ES line */
            for(i = 0; i < 32; i++)
                *q++ = SYNC_LEVEL;
            for(i = 0; i < 400; i++)
                *q++ = BLACK_LEVEL;
            for(i = 0; i < 369; i++)
                *q++ = SYNC_LEVEL;
            for(i = 0; i < 63; i++)
                *q++ = BLACK_LEVEL;
            break;
        case 319:
            /* EB line */
            for(i = 0; i < 32; i++)
                *q++ = SYNC_LEVEL;
            for(i = 0; i < 400; i++)
                *q++ = BLACK_LEVEL;
            for(i = 0; i < 432; i++)
                *q++ = BLACK_LEVEL;
            break;
        case 24 ... 311:
            /* VV line */
            s = img_data + ((y - 24) * 2) * linesize;
            goto gen_line;
        case 337 ... 623:
            /* VV line */
            s = img_data + ((y - 337) * 2 + 1) * linesize;
        gen_line:
            /* Front Porch */
            for(i = 0; i < 21; i++)
                *q++ = BLACK_LEVEL;
            /* horiz sync */
            for(i = 0; i < 64; i++)
                *q++ = SYNC_LEVEL;
            /* blank */
            for(i = 0; i < 75; i++)
                *q++ = BLACK_LEVEL;
            /* active video */
            for(i = 0; i < 704; i++) {
                v = (s[i] * (WHITE_LEVEL - BLACK_LEVEL)) / 255.0;
                *q++ = (long int)v + BLACK_LEVEL;
            }
            break;
        case 624:
            /* VE line */
            s = img_data + 575 * linesize;
            /* Front Porch */
            for(i = 0; i < 21; i++)
                *q++ = BLACK_LEVEL;
            /* horiz sync */
            for(i = 0; i < 64; i++)
                *q++ = SYNC_LEVEL;
            /* blank */
            for(i = 0; i < 75; i++)
                *q++ = BLACK_LEVEL;
            /* active video */
            for(i = 0; i < 272; i++) {
                v = (s[i] * (WHITE_LEVEL - BLACK_LEVEL)) / 255.0;
                *q++ = (long int)v + BLACK_LEVEL;
            }
            for(i = 0; i < 32; i++)
                *q++ = SYNC_LEVEL;
            for(i = 0; i < 400; i++)
                *q++ = BLACK_LEVEL;
            break;
        default:
            /* BB */
            /* Front Porch */
            for(i = 0; i < 21; i++)
                *q++ = BLACK_LEVEL;
            /* horiz sync */
            for(i = 0; i < 64; i++)
                *q++ = SYNC_LEVEL;
            /* blank */
            for(i = 0; i < 75; i++)
                *q++ = BLACK_LEVEL;
            for(i = 0; i < 704; i++) 
                *q++ = BLACK_LEVEL;
            break;
        }
    }
}

int main(int argc, char *argv[])
{
  int fpin, fpout;
  off_t filesize;
  off_t res;
  uint8_t *pal_signal = NULL, *img_data = NULL;

  if(argc < 3)
  {
    printf("please use %s inputfile.gray outputfile.gray\n", argv[0]);
    return 1;
  }
  fpin = open(argv[1], O_RDONLY);
  if(fpin < 0)
  {
    printf("input file %s could not be opened\n", argv[1]);
    return 2;
  }
  fpout = open(argv[2], O_CREAT|O_RDWR|O_TRUNC,S_IRUSR|S_IWUSR);
  if(fpout < 0)
  {
    printf("output file %s could not be opened\n", argv[2]);
    return 2;
  }
  filesize = lseek(fpin, 0L, SEEK_END);
  if(filesize < 0)
  {
    perror("fseek1");
    return 3;
  }
  res = lseek(fpin, (off_t)0, SEEK_SET);
  if(res < 0)
  {
    perror("fseek2");
    return 4;
  }

  printf("inputfile has %0ld bytes, expected size %0d\n", filesize, 704*576);

  pal_signal = calloc(864*625, 1);
  img_data   = calloc(704*576, 1);
  if((pal_signal == NULL)||(img_data == NULL))
  {
    printf("calloc error pal_signal: %lx, img_data: %lx\n", (long int)pal_signal, (long int)img_data);
    return 5;
  }
  if(read(fpin, img_data, filesize)<0)
  {
     perror("read");
     return 6;
  }
  picture_to_pal(pal_signal, img_data, 704);
  if(write(fpout, pal_signal, 864*625)<0)
  {
    perror("write");
    return 7;
  }

  free(pal_signal);
  free(img_data);
  return 0;
}
